/* Copyright (c) 2025 Beijing Semidrive Technology Corporation
 * SPDX-License-Identifier: Apache-2.0
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/********************************************************************************************************
 *                                      Include header files                                            *
 *******************************************************************************************************/
#include "Pfu.h"
#include "Pfu_IpFault.h"
#include "Pfu_IpRegs.h"

#if defined (CFG_PLATFORM_MCAL)
#include "RegHelper.h"
#include "Compiler.h"
/* PRQA S 0603,0602 1 */
#define __ASM   ASM
#define PFU_START_SEC_CODE
#include "Pfu_MemMap.h"
#elif defined (CFG_PLATFORM_SSDK)
#include "reg.h"
#else
#error "Undefined macros CFG_PLATFORM_MCAL or CFG_PLATFORM_SSDK"
#endif /** #if defined (CFG_PLATFORM_MCAL) */

/********************************************************************************************************
 *                                  Global Function Declarations                                        *
 *******************************************************************************************************/
/** *****************************************************************************************************
 * \brief PFU bypass function.
 *
 * \verbatim
 * Syntax             : lld_status_t Pfu_Ip_Bypass(uint32_t base, bool bypassEn, uint32_t timeout)
 *
 * Service ID[hex]    : None
 *
 * Sync/Async         : Synchronous
 *
 * Reentrancy         : Non reentrant
 *
 * Parameters (in)    : base - pfu base address
 *                      bypassEn - true : bypass
 *                                 false : pfu enable
 *                      timeout - timeout val.If timeout = 0 use the internal
 *                              PFU_POLLING_TIMEOUT macro as a timeout cnt
 *
 * Parameters (inout) : None
 *
 * Parameters (out)   : None
 *
 * Return value       : error code
 *
 * Description        : This interface requires the CPU's L2 interface to be idle
 *                      in order not to return a timeout error. There are two ways
 *                      to do this:
 *                      1. Open the cache to ensure that both the code segment and
 *                      the stack need to be placed in the cacheable.
 *                      2. Configure the corresponding CPU's PFU before the CPU kick.
 * \endverbatim
 *******************************************************************************************************/
lld_status_t Pfu_Ip_Bypass(uint32_t base, bool bypassEn, uint32_t timeout)
{
    /* PRQA S 1257 1 */
    lld_status_t errorId = 0UL;
    uint32_t regVal = readl(base + PFU_MODE_CTRL_OFF);
    uint32_t timeoutVal = timeout;

    if (0UL == timeout)
    {
        timeoutVal = PFU_POLLING_TIMEOUT;
    } /** else not need */

    PfuHook_Pfu_Timeout();

    /* PRQA S 1881 1 */
    if (true == bypassEn)
    {
        regVal |= BM_PFU_MODE_CTRL_BYPASS_MODE;
    }
    else
    {
        regVal &= (~BM_PFU_MODE_CTRL_BYPASS_MODE);
    }

    writel(regVal, base + PFU_MODE_CTRL_OFF);
    /* PRQA S 3112,0907,0911,2753,0926,0873,0665,0920 1 */
    __ASM volatile("dsb sy"::: "memory");
    regVal = readl(base + PFU_MODE_CTRL_OFF);

    /** #10 You need to wait for the CPU L2 interface bus to be idle before you can set the bit. */
    while ((BM_PFU_MODE_CTRL_MODE_SWITCH_DONE != (regVal & BM_PFU_MODE_CTRL_MODE_SWITCH_DONE)) &&
            (0UL != timeoutVal))
    {
        regVal = readl(base + PFU_MODE_CTRL_OFF);
        timeoutVal--;
    }

    if (0UL == timeoutVal)
    {
        errorId = PFU_E_TIMEOU;
    } /** else not needed */

    return errorId;
}

/** *****************************************************************************************************
 * \brief Refresh region space function.
 *
 * \verbatim
 * Syntax             : void Pfu_Ip_Flush(uint32_t base)
 *
 * Service ID[hex]    : None
 *
 * Sync/Async         : Synchronous
 *
 * Reentrancy         : Non reentrant
 *
 * Parameters (in)    : base - pfu base address
 *
 * Parameters (inout) : None
 *
 * Parameters (out)   : None
 *
 * Return value       : error code
 *
 * Description        : This interface requires the CPU's L2 interface to be idle
 *                      in order not to return a timeout error. There are two ways
 *                      to do this:
 *                      1. Open the cache to ensure that both the code segment and
 *                      the stack need to be placed in the cacheable.
 *                      2. Configure the corresponding CPU's PFU before the CPU kick.
 *                      Enable bypass function CPU will not prefetch, but the read data
 *                      will be put into PFU buff.
 * \endverbatim
 *******************************************************************************************************/
void Pfu_Ip_Flush(uint32_t base)
{
    uint32_t regVal;

    regVal = readl(base + PFU_RBUF_CTRL_OFF);
    regVal |= BM_PFU_RBUF_CTRL_RBUF_FLUSH;
    writel(regVal, base + PFU_RBUF_CTRL_OFF);
    regVal &= (~BM_PFU_RBUF_CTRL_RBUF_FLUSH);
    writel(regVal, base + PFU_RBUF_CTRL_OFF);
}

/** *****************************************************************************************************
 * \brief prefetch en function.
 *
 * \verbatim
 * Syntax             : uint8_t Pfu_Ip_PrefetchEn(uint32_t base, bool prefetchEn)
 *
 * Service ID[hex]    : None
 *
 * Sync/Async         : Synchronous
 *
 * Reentrancy         : Non reentrant
 *
 * Parameters (in)    : base - pfu base address
 *                      prefetchEn - prefetch en
 *
 * Parameters (inout) : None
 *
 * Parameters (out)   : None
 *
 * Return value       : None
 *
 * Description        : Turns off and on the CPU prefetch function.
 *                      You need to enable bypass to operate this interface.
 * \endverbatim
 *******************************************************************************************************/
void Pfu_Ip_PrefetchEn(uint32_t base, bool prefetchEn)
{
    uint32_t regVal = readl(base + PFU_RBUF_CTRL_OFF);

    /* PRQA S 1881 1 */
    if (true == prefetchEn)
    {
        regVal |= BM_PFU_RBUF_CTRL_PREF_EN;
    }
    else
    {
        regVal &= (~BM_PFU_RBUF_CTRL_PREF_EN);
    }

    writel(regVal, base + PFU_RBUF_CTRL_OFF);
}

/** *****************************************************************************************************
 * \brief Configuring the Region Function Interface.
 *
 * \verbatim
 * Syntax             : lld_status_t Pfu_Ip_RegionConfig(uint32_t base,
 *                                                  const Pfu_Ip_RegionCfgType *cfg)
 *
 * Service ID[hex]    : None
 *
 * Sync/Async         : Synchronous
 *
 * Reentrancy         : Non reentrant
 *
 * Parameters (in)    : base - pfu base address
 *                      cfg - region config
 *
 * Parameters (inout) : None
 *
 * Parameters (out)   : None
 *
 * Return value       : error code
 *
 * Description        : Configuring the Region Function Interface.
 * \endverbatim
 *******************************************************************************************************/
lld_status_t Pfu_Ip_RegionConfig(uint32_t base, const Pfu_Ip_RegionCfgType *cfg)
{
    /* PRQA S 1257 1 */
    lld_status_t errorId = 0UL;

    if ((((cfg->startAddr) & PFU_4K_ALIGNMENT) > 0UL) ||
        (((cfg->endAddr) & PFU_4K_ALIGNMENT) > 0UL))
    {
        errorId = PFU_E_ALIGNMENT;
    }
    else
    {
        /* PRQA S 4521 4 */
        writel(PFU_PREF_REGION_ALIGNMENT(cfg->startAddr),
                base + PFU_PREF_REGION_START_OFF(cfg->regionId));
        writel(PFU_PREF_REGION_ALIGNMENT(cfg->endAddr),
                base + PFU_PREF_REGION_END_OFF(cfg->regionId));
    }

    return errorId;
}

/** *****************************************************************************************************
 * \brief Interrupt Configuration Function Interface.
 *
 * \verbatim
 * Syntax             : void Pfu_Ip_InterruptConfig(uint32_t base,
 *                                          const Pfu_Ip_InterruptCfgType *cfg)
 *
 * Service ID[hex]    : None
 *
 * Sync/Async         : Synchronous
 *
 * Reentrancy         : Non reentrant
 *
 * Parameters (in)    : base - pfu base address
 *                      cfg - interrupt config
 *
 * Parameters (inout) : None
 *
 * Parameters (out)   : None
 *
 * Return value       : None
 *
 * Description        : Interrupt Configuration Function Interface.
 * \endverbatim
 *******************************************************************************************************/
void Pfu_Ip_InterruptConfig(uint32_t base, const Pfu_Ip_InterruptCfgType *cfg)
{
    uint32_t regVal = readl(base + PFU_INT_SIG_EN_OFF);

    /* PRQA S 1881 1 */
    if (true == cfg->responseErr)
    {
        regVal |= BM_PFU_INT_SIG_EN_RESP_ERROR;
    }
    else
    {
        regVal &= (~BM_PFU_INT_SIG_EN_RESP_ERROR);
    }

    /* PRQA S 1881 1 */
    if (true == cfg->axiMstTimeout)
    {
        regVal |= BM_PFU_INT_SIG_EN_AXI_MST_TIMEOUT;
    }
    else
    {
        regVal &= (~BM_PFU_INT_SIG_EN_AXI_MST_TIMEOUT);
    }

    /* PRQA S 1881 1 */
    if (true == cfg->axiSlvTimeout)
    {
        regVal |= BM_PFU_INT_SIG_EN_AXI_SLV_TIMEOUT;
    }
    else
    {
        regVal &= (~BM_PFU_INT_SIG_EN_AXI_SLV_TIMEOUT);
    }
    writel(0x07UL, base + PFU_INT_STAT_OFF);
    writel(regVal, base + PFU_INT_SIG_EN_OFF);
    writel(regVal, base + PFU_INT_STAT_EN_OFF);
}

/** *****************************************************************************************************
 * \brief Get Interrupt Status Function Interface.
 *
 * \verbatim
 * Syntax             : uint32_t Pfu_Ip_GetInterruptSta(uint32_t base)
 *
 * Service ID[hex]    : None
 *
 * Sync/Async         : Synchronous
 *
 * Reentrancy         : Non reentrant
 *
 * Parameters (in)    : base - pfu base address
 *
 * Parameters (inout) : None
 *
 * Parameters (out)   : None
 *
 * Return value       : interrupt raw data
 *                      The meaning of the corresponding bit can be seen in the
 *                      type Pfu_Ip_InterruptType.
 *
 * Description        : Get Interrupt Status Function Interface.
 * \endverbatim
 *******************************************************************************************************/
uint32_t Pfu_Ip_GetInterruptSta(uint32_t base)
{
    return readl(base + PFU_INT_STAT_OFF);
}

/** *****************************************************************************************************
 * \brief clear interrupt.
 *
 * \verbatim
 * Syntax             : void Pfu_Ip_InterruptClr(uint32_t base, uint32_t msk)
 *
 * Service ID[hex]    : None
 *
 * Sync/Async         : Synchronous
 *
 * Reentrancy         : Non reentrant
 *
 * Parameters (in)    : base - pfu base address
 *                      msk - Can be one or more of the enumerated types Pfu_Ip_InterruptType.
 *
 * Parameters (inout) : None
 *
 * Parameters (out)   : None
 *
 * Return value       : None
 *
 * Description        : clear interrupt.
 * \endverbatim
 *******************************************************************************************************/
void Pfu_Ip_InterruptClr(uint32_t base, uint32_t msk)
{
    writel(msk, base + PFU_INT_STAT_OFF);
}

/** *****************************************************************************************************
 * \brief config axi master timeout
 *
 * \verbatim
 * Syntax             : void Pfu_Ip_AxiMstTimeoutCfg(uint32_t base, uint32_t timeout)
 *
 * Service ID[hex]    : None
 *
 * Sync/Async         : Synchronous
 *
 * Reentrancy         : Non reentrant
 *
 * Parameters (in)    : base - pfu base address
 *                      timeout - timeout val(The formula is as follows: us = timeout /axi_clk)
 *
 * Parameters (inout) : None
 *
 * Parameters (out)   : None
 *
 * Return value       : None
 *
 * Description        : config axi master timeout
 *                      The formula is as follows: us = timeout /axi_clk
 * \endverbatim
 *******************************************************************************************************/
void Pfu_Ip_AxiMstTimeoutCfg(uint32_t base, uint32_t timeout)
{
    uint32_t regVal = readl(base + PFU_AXI_MST_CTRL_OFF);
    regVal &= (~BM_PFU_AXI_MST_CTRL_TIMEOUT_EN);
    writel(regVal, base + PFU_AXI_MST_CTRL_OFF);
    writel(timeout, base + PFU_AXI_MST_TIMEOUT_OFF);
    regVal |= (BM_PFU_AXI_MST_CTRL_TIMEOUT_EN);
    writel(regVal, base + PFU_AXI_MST_CTRL_OFF);
}

/** *****************************************************************************************************
 * \brief config axi master timeout
 *
 * \verbatim
 * Syntax             : void Pfu_Ip_AxiSlvTimeoutCfg(uint32_t base, uint32_t timeout)
 *
 * Service ID[hex]    : None
 *
 * Sync/Async         : Synchronous
 *
 * Reentrancy         : Non reentrant
 *
 * Parameters (in)    : base - pfu base address
 *                      timeout - timeout val(The formula is as follows: us = timeout /axi_clk)
 *
 * Parameters (inout) : None
 *
 * Parameters (out)   : None
 *
 * Return value       : None
 *
 * Description        : config axi master timeout
 *                      The formula is as follows: us = timeout /axi_clk
 * \endverbatim
 *******************************************************************************************************/
void Pfu_Ip_AxiSlvTimeoutCfg(uint32_t base, uint32_t timeout)
{
    uint32_t regVal = readl(base + PFU_AXI_SLV_CTRL_OFF);
    regVal &= (~BM_PFU_AXI_SLV_CTRL_TIMEOUT_EN);
    writel(regVal, base + PFU_AXI_SLV_CTRL_OFF);
    writel(timeout, base + PFU_AXI_SLV_TIMEOUT_OFF);
    regVal |= (BM_PFU_AXI_SLV_CTRL_TIMEOUT_EN);
    writel(regVal, base + PFU_AXI_SLV_CTRL_OFF);
}

#if defined (CFG_PLATFORM_MCAL)
#define PFU_STOP_SEC_CODE
#include "Pfu_MemMap.h"
#elif defined (CFG_PLATFORM_SSDK)
#else
#error "Undefined macros CFG_PLATFORM_MCAL or CFG_PLATFORM_SSDK"
#endif /** #if defined (CFG_PLATFORM_MCAL) */

/* End of file */
